using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace RayDen.RayEngine.Core.Devices
{
    public abstract class EntityFactory<TEntity, TEntityContext>
    {
        public abstract TEntity Produce( TEntityContext context );
    }

    public abstract class WorkingQueueDevice<TData>
    {
        protected ConcurrentQueue<TData> todoBuffer, doneBuffer;
        protected Task evaluationTask;
        protected CancellationTokenSource cancelToken;

        protected Stopwatch wallclock;
        protected bool started;
        protected double statsDeviceIdleTime;
        protected double statsDeviceTotalTime;

        protected WorkingQueueDevice()
        {
            todoBuffer = new ConcurrentQueue<TData>();
            doneBuffer = new ConcurrentQueue<TData>();
            wallclock = new Stopwatch();
            cancelToken = new CancellationTokenSource();
        }
        public void Stop()
        {
            wallclock.Stop();
            started = false;

            if (evaluationTask.Status == TaskStatus.Running)
            {
                cancelToken.Cancel();
                evaluationTask.Dispose();
                evaluationTask = null;
            }

            todoBuffer = new ConcurrentQueue<TData>();
            doneBuffer = new ConcurrentQueue<TData>();
        }

        public void Interrupt()
        {
            wallclock.Stop();
            if (this.evaluationTask.Status == TaskStatus.Running)
            {
                cancelToken.Cancel();
            }
        }

        public void Start()
        {
            if (started)
                return;
            started = true;
            wallclock.Start();
            statsDeviceIdleTime = 0.0;
            statsDeviceTotalTime = 0.0;
            cancelToken = new CancellationTokenSource();
            this.evaluationTask = Task.Factory.StartNew(() => Eval(cancelToken.Token, this), cancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
        }

        protected static void Eval(CancellationToken ct, WorkingQueueDevice<TData> device)
        {
            Console.WriteLine("{0}", device.GetType().Name);
            while (!ct.IsCancellationRequested)
            {
                //var t1 = 0;// device.wallclock.ElapsedTicks;
                TData pathInfoBuffer;

                if (!device.todoBuffer.TryDequeue(out pathInfoBuffer))
                {
                    continue;
                }

                //var t2 = DateTime.UtcNow.Ticks;

                device.Process(pathInfoBuffer);


                //Console.WriteLine("Result returns {0} items ", buffer.rayHits.Length);
                //            Console.WriteLine("Intersections {0} items ", buffer.rayHits.Count(p => p.index != uint.MaxValue));
                //var t3 = device.wallclock.ElapsedTicks;

                //device.statsDeviceIdleTime += t2 - t1;
                //device.statsDeviceTotalTime += t3 - t1;
                //device.statsTotalRayCount += rayBuffer.Count();
                device.doneBuffer.Enqueue(pathInfoBuffer);
            }
        }

        protected abstract void Process(TData data);


        public void PushRayBuffer(TData data)
        {
            todoBuffer.Enqueue(data);
        }

        public TData PopRayBuffer()
        {
            TData result;
            doneBuffer.TryDequeue(out result);
            return result;
        }
    };
}